#include "buffermanager.h"
#include "block.h"
#include "SysException.h"
#include <string>
#include <cstdio>
#include <cstdlib>

using namespace std;

//const string prefix = "data\\";

BufferManager::BufferManager()
{
	head = tail = NULL;
	block_num = 0;
	isfull = false;
}

BufferManager::~BufferManager()
{
	pointer = head;
	while (pointer)
	{
		if (pointer->block.dirtybit)
			writethroughblock(pointer->block);
		head = pointer->next;
		delete pointer;
		pointer = head;
	}
}

bool BufferManager::writethroughblock(const Block &block)
{
	FILE *fout = fopen(block.filename.c_str(), "rb+");
	if (!fout)
		fout = fopen(block.filename.c_str(), "wb+");
	if (!fout)
		return false;
	fseek(fout, block.offset * BLOCK_SIZE, SEEK_SET);
	fwrite(block.block, BLOCK_SIZE, sizeof(byte), fout);
	fclose(fout);
	return true;
}

bool BufferManager::readblock(string filename, int fileoffset, Block &block)
{
	block.filename = filename;
	block.offset = fileoffset;
	FILE *fin = fopen(block.filename.c_str(), "rb+");
	if (!fin)
		return false;
	fseek(fin, block.offset * BLOCK_SIZE, SEEK_SET);
	fread(block.block, BLOCK_SIZE, sizeof(byte), fin);
	fclose(fin);
	return true;
}

Block BufferManager::readBlock(std::string filename, int fileoffset)
{
	pointer = head;
	while (pointer)
	{
		if (pointer->block.filename == filename && pointer->block.offset == fileoffset)
		{
			if (pointer == head)
				;
			else
			{
				if (tail == pointer)
					tail = last;
				last->next = pointer->next;
				pointer->next = head;
				head = pointer;
			}
			return pointer->block;
		}				
		if (pointer->next == NULL)
			break;
		last = pointer;
		pointer = pointer->next;
	}
	if (isfull)
	{
		if (tail->block.dirtybit)
			writethroughblock(tail->block);
		tail = last;
		delete tail->next;
		tail->next = NULL;
		pointer = head;
		head = new list;
		if (readblock(filename, fileoffset, head->block))
			throw(CSysException("no file " + filename + " found"), FILE_NOT_FOUNT_ERROR);
		head->next = pointer;
		return head->block;
	}
	else 
	{
		if (head == NULL)
		{
			tail = head = pointer = new list;
			if (readblock(filename, fileoffset, head->block))
				throw(CSysException("no file " + filename + " found"), FILE_NOT_FOUNT_ERROR);
		}
		else
		{
			pointer = head;
			head = new list;
			if (readblock(filename, fileoffset, head->block))
				throw(CSysException("no file " + filename + " found"), FILE_NOT_FOUNT_ERROR);
			head->next = pointer;
		}
		block_num++;
		if (block_num == BLOCK_NUM)
			isfull = true;
		return head->block;
	}
}

bool BufferManager::writeBlock(string filename, int fileoffset, Block block)
{
	pointer = head;
	block.filename = filename;
	block.offset = fileoffset;
	while (pointer)
	{
		if (pointer->block.filename == filename && pointer->block.offset == fileoffset)
		{
			if (pointer == head)
				pointer->block = block;
			else
			{
				if (tail == pointer)
					tail = last;
				last->next = pointer->next;
				pointer->next = head;
				head = pointer;
				pointer->block = block;
			}
			pointer->block.dirtybit = 1;
			return true;
		}
		if (pointer->next == NULL)
			break;
		last = pointer;
		pointer = pointer->next;
	}
	if (isfull)
	{
		if (tail->block.dirtybit)
			writethroughblock(tail->block);
		tail = last;
		delete tail->next;
		tail->next = NULL;
		pointer = head;
		head = new list;
		head->block = block;
		head->block.dirtybit = 1;
		head->next = pointer;
	}
	else 
	{
		if (head == NULL)
		{
			tail = head = pointer = new list;
			head->block = block;
			head->block.dirtybit = 1;
		}
		else
		{
			pointer = head;
			head = new list;
			head->block = block;
			head->block.dirtybit = 1;
			head->next = pointer;
		}
		block_num++;
		if (block_num == BLOCK_NUM)
			isfull = true;
	}
	return true;
}

bool BufferManager::writeThroughBlock(string filename, int fileoffset, Block block)
{
	pointer = head;
	block.filename = filename;
	block.offset = fileoffset;
	while (pointer)
	{
		if (pointer->block.filename == filename && pointer->block.offset == fileoffset)
		{
			if (pointer == head)
				pointer->block = block;
			else
			{
				if (tail == pointer)
					tail = last;
				last->next = pointer->next;
				pointer->next = head;
				head = pointer;
				pointer->block = block;
			}
			writethroughblock(pointer->block);
			return true;
		}
		if (pointer->next == NULL)
			break;
		last = pointer;
		pointer = pointer->next;
	}
	if (isfull)
	{
		if (tail->block.dirtybit)
			writethroughblock(tail->block);
		tail = last;
		delete tail->next;
		tail->next = NULL;
		pointer = head;
		head = new list;
		head->block = block;
		writethroughblock(head->block);
		head->next = pointer;
	}
	else 
	{
		if (head == NULL)
		{
			tail = head = pointer = new list;
			head->block = block;
			writethroughblock(head->block);
		}
		else
		{
			pointer = head;
			head = new list;
			head->block = block;
			writethroughblock(head->block);
			head->next = pointer;
		}
		block_num++;
		if (block_num == BLOCK_NUM)
			isfull = true;
	}
	return true;
}

bool BufferManager::clearBlock(string filename, int fileoffset)
{
	Block block;
	writeBlock(filename, fileoffset, block);
	return true;
}

bool BufferManager::deleteFile(string filename)
{
	pointer = head;
	list *p = NULL;
	while (pointer)
	{
		if (pointer->block.filename == filename)
		{
			if (p == NULL)
				head = pointer->next;
			else
				p->next = pointer->next;
			delete pointer;
			pointer = head->next;
		}
	}
}
/*
int main()
{
	{
		BufferManager buffer;
		Block block[16];
		for (int i = 0; i != 16; ++i)
		{
			block[i].block[0] = i;
			block[i].block[1] = i;
			block[i].block[2] = i;
			block[i].block[3] = i;
			buffer.writeBlock("123", i, block[i]); 
			buffer.showDetails();
		}
		buffer.readBlock("123", 1);
		buffer.showDetails();
		buffer.readBlock("123", 15);
		buffer.showDetails();
		buffer.writeBlock("123", 12, block[11]);
		buffer.showDetails();
		block[1].writeVarchar("123", 0);
		cout << block[1].readVarchar(0);
	}
	system("pause");
}*/